﻿/*
 * Module Name: Lat Long Calculator.cs
 * 
 * Purpose: This class is used calcluate common lat long functions
 * 
 * Created by: Chris Tisdale
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common_Code.Heler_Functions;

namespace Math_Functions
{
    /*
     * This is a structor for a lat long in Degrees Mins Seconds
     */
    public struct latLongDMS 
    {
        public double latDegree, latMin, latSec;
        public double LongDegree, LongMin, LongSec;

        public latLongDMS(double latD, double latM, double latS, double longD, double longM, double longS)
        {
            latDegree = latD;
            latMin = latM;
            latSec = latS;
            LongDegree = longD;
            LongMin = longM;
            LongSec = longS;
        }
    }

    /*
     * This is a structor for a lat long in decimal
     */
    public struct latLongDec
    {
        public double lat, Long;

        public latLongDec(double lat, double Long)
        {
            this.lat = lat;
            this.Long = Long;
        }
    }

    /*
     * This is a emuneration of different calculation methods
     */
    public enum latLongCalMethod
    {
        VincentyWGS84 = 0,
        VincentyGRS80,
        VincentyAiry1830,
        VincentyInternatl1924,
        VincentyClarkeMod1880,
        VincentyGRS67,
        Haversine
    }

    /*
     * This is a emuneration of different calculation methods
     */
    public enum latLongCalType
    {
        KM = 0,
        NM,
        M,
        FT
    }

    class Lat_Long_Calculator
    {
        private const latLongCalMethod DEFAULTMETHOD = latLongCalMethod.Haversine;
        private const latLongCalType DEFAULTTYPE = latLongCalType.NM;

        /*
         * Name: CalLatLongDist
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         *             latLongCalType calType (defaulted to Nautical Miles)
         * 
         * Returns: double dist
         * 
         * Purpose:  Calculates distance between two lat longs
         */
        public static double CalLatLongDist(latLongDMS latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD, latLongCalType calType = DEFAULTTYPE)
        {
            var temp1 = ConvertLatLongDMS(latLong1);
            var temp2 = ConvertLatLongDMS(latLong2);
            return CalLatLongDist(temp1, temp2, calMethod, calType);
        }

        /*
         * Name: CalLatLongDist
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         *             latLongCalType calType (defaulted to Nautical Miles)
         * 
         * Returns: double dist
         * 
         * Purpose:  Calculates distance between two lat longs
         */
        public static double CalLatLongDist(latLongDMS latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD, latLongCalType calType = DEFAULTTYPE)
        {
            var temp = ConvertLatLongDMS(latLong1);
            return CalLatLongDist(temp, latLong2, calMethod, calType);
        }

        /*
         * Name: CalLatLongDist
         * 
         * Parameters: latLongDec latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         *             latLongCalType calType (defaulted to Nautical Miles)
         * 
         * Returns: double dist
         * 
         * Purpose:  Calculates distance between two lat longs
         */
        public static double CalLatLongDist(latLongDec latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD, latLongCalType calType = DEFAULTTYPE)
        {
            var temp = ConvertLatLongDMS(latLong2);
            return CalLatLongDist(latLong1, temp, calMethod, calType);
        }

        /*
         * Name: CalLatLongDist
         * 
         * Parameters: latLongDec latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         *             latLongCalType calType (defaulted to Nautical Miles)
         * 
         * Returns: double dist
         * 
         * Purpose:  Calculates distance between two lat longs
         */
        public static double CalLatLongDist(latLongDec latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD, latLongCalType calType = DEFAULTTYPE)
        {
            double dist = 0;
            //Is this a Vincenty Method the type of GPS only varies the a, b, and f values
            if (calMethod == latLongCalMethod.VincentyAiry1830 || calMethod == latLongCalMethod.VincentyClarkeMod1880 ||
                calMethod == latLongCalMethod.VincentyGRS67 || calMethod == latLongCalMethod.VincentyGRS80 ||
                calMethod == latLongCalMethod.VincentyInternatl1924 || calMethod == latLongCalMethod.VincentyWGS84)
            {
                double a, b, f, A, B, tanU1, cosU1, lamb, sinAlpha, cosAlpha, tempTheta, theta, cos2A, cos2theta, dlong, RadLat1, RadLat2;
                if (calMethod == latLongCalMethod.VincentyWGS84)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                }
                else if (calMethod == latLongCalMethod.VincentyAiry1830)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                    throw new NotImplementedException();
                }
                else if (calMethod == latLongCalMethod.VincentyClarkeMod1880)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                    throw new NotImplementedException();
                }
                else if (calMethod == latLongCalMethod.VincentyGRS67)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                    throw new NotImplementedException();
                }
                else if (calMethod == latLongCalMethod.VincentyGRS80)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                    throw new NotImplementedException();
                }
                else if (calMethod == latLongCalMethod.VincentyInternatl1924)
                {
                    a = 6378137;
                    f = 1 / 298.257223563;
                    b = (1 - f) * a;
                    throw new NotImplementedException();
                }
                else
                {
                    throw new NotSupportedException();
                }

                tanU1 = (1 - f) * Math.Tan(HelperClass.DegreesToRad(latLong1.lat));
                //Finish Formula
                

                if (calType == latLongCalType.KM)
                {
                    //Convert to KM
                }
                else if (calType == latLongCalType.M)
                {
                    //Do nothing
                }
                else if (calType == latLongCalType.FT)
                {
                    //Convert to FT
                }
                else if (calType == latLongCalType.NM)
                {
                    //Convert to NM
                }
                else
                {
                    throw new NotSupportedException();
                }
                throw new NotImplementedException();
            }
            else if (calMethod == latLongCalMethod.Haversine) //Is this the Haversine method
            {
                double R, dlat, dlong, RadLat1, RadLat2, a, c;
                if (calType == latLongCalType.KM)
                {
                    R = 6378.1;
                }
                else if (calType == latLongCalType.M)
                {
                    R = 6378100;
                }
                else if (calType == latLongCalType.FT)
                {
                    R = 20925524.9;
                }
                else if (calType == latLongCalType.NM)
                {
                    R = 3443.89849;
                }
                else
                {
                    throw new NotSupportedException();
                }
                dlat = HelperClass.DegreesToRad((latLong1.lat - latLong2.lat));
                dlong = HelperClass.DegreesToRad((latLong1.Long - latLong2.Long));
                RadLat1 = HelperClass.DegreesToRad(latLong1.lat);
                RadLat2 = HelperClass.DegreesToRad(latLong2.lat);
                a = Math.Sin(dlat / 2) * Math.Sin(dlat / 2) + Math.Sin(dlong / 2) * 
                    Math.Sin(dlong / 2) * Math.Cos(RadLat1) * Math.Cos(RadLat2);
                c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
                dist = R * c;
            }
            else //there is no other method supported
            {
                throw new NotSupportedException();
            }
            return dist;
        }

        /*
         * Name: CalBearing
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: double bearing
         * 
         * Purpose:  Calculates bearing between two lat longs
         */
        public static double CalBearing(latLongDMS latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalBearing
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: double bearing
         * 
         * Purpose:  Calculates bearing between two lat longs
         */
        public static double CalBearing(latLongDMS latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalBearing
         * 
         * Parameters: latLongDec latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: double bearing
         * 
         * Purpose:  Calculates bearing between two lat longs
         */
        public static double CalBearing(latLongDec latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalBearing
         * 
         * Parameters: latLongDec latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: double bearing
         * 
         * Purpose:  Calculates bearing between two lat longs
         */
        public static double CalBearing(latLongDec latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: ConvertLatLongDMS
         * 
         * Parameters: latLongDMS latLong
         * 
         * Returns: latLongDec lat long in Decimal
         * 
         * Purpose:  Converts lat long into Decimal
         */
        public static latLongDec ConvertLatLongDMS(latLongDMS latLong)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: ConvertLatLongDMS
         * 
         * Parameters: latLongDec latLong
         * 
         * Returns: latLongDMS lat long in degrees mins seconds
         * 
         * Purpose:  Converts lat long into Degrees Mins Seconds
         */
        public static latLongDMS ConvertLatLongDec(latLongDec latLong)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalMidPoint
         * 
         * Parameters: latLongDec latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: latLongDec MidPoint
         * 
         * Purpose:  Calculates midpoint between two lat longs
         */
        public static latLongDec CalMidPoint(latLongDec latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalMidPoint
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: latLongDMS MidPoint
         * 
         * Purpose:  Calculates midpoint between two lat longs
         */
        public static latLongDMS CalMidPoint(latLongDMS latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalMidPoint
         * 
         * Parameters: latLongDMS latLong1
         *             latLongDec latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: latLongDMS MidPoint
         * 
         * Purpose:  Calculates midpoint between two lat longs
         */
        public static latLongDMS CalMidPoint(latLongDMS latLong1, latLongDec latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }

        /*
         * Name: CalMidPoint
         * 
         * Parameters: latLongDec latLong1
         *             latLongDMS latLong2
         *             latLongCalMethod calMethod (defaulted to Haversine)
         * 
         * Returns: latLongDMS MidPoint
         * 
         * Purpose:  Calculates midpoint between two lat longs
         */
        public static latLongDMS CalMidPoint(latLongDec latLong1, latLongDMS latLong2,
            latLongCalMethod calMethod = DEFAULTMETHOD)
        {
            throw new NotImplementedException();
        }
    }
}
